package cn.mygweb.common.shiro;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.apache.shiro.codec.Base64;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.thymeleaf.util.StringUtils;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import cn.mygweb.admin.modules.sys.pojo.Menu;
import cn.mygweb.admin.modules.sys.service.MenuService;

/** 
* <p>Title: ShiroConfig.java</p>
* <p>Description: shiro配置类</p>  
* @author gxb 
* @date 2019年6月2日 
* <p>Copyright: Copyright (c) 2019</p>
* <p>Company: www.mygweb.cn</p> 
*/
@Configuration
public class ShiroConfig {

	@Autowired
	private MenuService menuService;
	
	@Value("${shiro.config.anon}")
	private String shiroConfigAnon;
	
	@Value("${shiro.config.user}")
	private String shiroConfigUser;
	
	@Value("${shiro.config.authc}")
	private String shiroConfigAuthc;
	
	@Value("${shiro.config.loginUrl}")
	private String shiroConfigLoginUrl;
	
	@Value("${shiro.config.unauthorizedUrl}")
	private String shiroConfigUnauthorizedUrl;
	
	/**
	 * <p>Title: getShiroFilterFactoryBean</p>
	 * <p>Description: 创建ShiroFilterFactoryBean</p>
	 * @return
	 */
	@Bean
	public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
		ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
		
		//设置安全管理器
		shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
		
		/* 
		 * 添加shiro内置过滤器,shiro内置过滤器，可以实现权限相关的拦截器，常用过滤器有：
		 * 	anon：	无需认证（登录）可以访问
		 * 	authc：	必须认证才可以访问
		 * 	user：	如果使用rememberMe的功能，可以直接访问
		 * 	perms：	该资源必须得到资源权限才可以访问
		 * 	role：	该资源必须得到角色权限才可以访问
		 */
		//为了保证添加的顺序，使用LinkedHashMap
		Map<String, String> filterMap = new LinkedHashMap<String, String>();
		//Map<String, String> --> Map<"访问路径"， "权限配置">
		if(!StringUtils.isEmptyOrWhitespace(shiroConfigAnon)) {
			String[] anonParams = shiroConfigAnon.split(",");
			for (String anonParam : anonParams) {
				filterMap.put(anonParam, "anon");
			}
		}
		
		//查询数据库菜单栏权限信息，并设置用户授权过滤器
		//Map<String, String> --> Map<"访问路径"， "perms[权限字符串]">，
		//例如： filterMap.put("/admin/index", "perms[admin:index]");
		List<Menu> menuList = menuService.queryMenuList(new Menu());
		for (Menu menu : menuList) {
			if(!StringUtils.isEmptyOrWhitespace(menu.getPath()) && !StringUtils.isEmptyOrWhitespace(menu.getPerm())
					&& null != menu.getPerm()) {
				filterMap.put(menu.getPath(), "perms[" + menu.getPerm() + "]");
			}
		}

		//admin下面所有的页面都必须经过认证才可以查看，这个需要放到最下面
		//authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
		//filterMap.put("/admin/**", "authc");
		//filterMap.put("/admin/**", "user");
		if(!StringUtils.isEmptyOrWhitespace(shiroConfigAuthc)) {
			String[] authcParams = shiroConfigAuthc.split(",");
			for (String authcParam : authcParams) {
				filterMap.put(authcParam, "authc");
			}
		}
		
		if(!StringUtils.isEmptyOrWhitespace(shiroConfigUser)) {
			String[] userParams = shiroConfigUser.split(",");
			for (String userParam : userParams) {
				filterMap.put(userParam, "user");
			}
		}
		
		shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
		
		//设置跳转的登录页面，不设置的话默认为login.jsp
		shiroFilterFactoryBean.setLoginUrl(shiroConfigLoginUrl);
		
		//设置无授权时跳转的页面
		shiroFilterFactoryBean.setUnauthorizedUrl(shiroConfigUnauthorizedUrl);
		
		return shiroFilterFactoryBean;
	}
	
	/**
	 * <p>Title: getDefaultWebSecurityManager</p>
	 * <p>Description: 创建DefaultWebSecurityManager</p>
	 * @return
	 */
	@Bean
	public DefaultWebSecurityManager getDefaultWebSecurityManager() {
		DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
		//关联realm
		securityManager.setRealm(getRealm());
		//配置记住我
		securityManager.setRememberMeManager(rememberMeManager());
		return securityManager;
	}
	
	/**
	 * <p>Title: getRealm</p>
	 * <p>Description: 创建Realm</p>
	 * @return
	 */
	@Bean
	public UserRealm getRealm() {
		return new UserRealm();
	}
	
	/**
	 * <p>Title: getShiroDialect</p>
	 * <p>Description: 配置ShiroDialect，用于thymeleaf和shiro标签配合使用</p>
	 * @return
	 */
	@Bean
	public ShiroDialect getShiroDialect() {
		return new ShiroDialect();
	}
	
	/**
	 * <p>Title: formAuthenticationFilter</p>
	 * <p>Description: FormAuthenticationFilter过滤器，过滤记住我</p>
	 * @return
	 */
	@Bean
	public FormAuthenticationFilter formAuthenticationFilter() {
		FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
		//对应前端的记住我选项的input checkbox name=rememberMe
		formAuthenticationFilter.setRememberMeParam("rememberMe");
		return formAuthenticationFilter;
	}
	
	/**
	 * <p>Title: rememberMeManager</p>
	 * <p>Description: cookie管理对象，记住我功能，rememberMe管理器</p>
	 * @return
	 */
	@Bean
	public CookieRememberMeManager rememberMeManager() {
		CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
		cookieRememberMeManager.setCookie(rememberMeCookie());
		//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
		cookieRememberMeManager.setCipherKey(Base64.decode("1sbTY5FLUs0KTA3Kprsdag=="));
		return cookieRememberMeManager;
	}
	
	/**
	 * <p>Title: rememberMeCookie</p>
	 * <p>Description: cookie对象，会话Cookie模板，默认为JSESSIONID
	 * 	问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe，自定义</p>
	 * @return
	 */
	@Bean
	public SimpleCookie rememberMeCookie() {
		//这个参数是cookie的名称，对应前端的checkbox的name=rememberMe
		SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
		//setcookie的httponly属性如果设为true的话，会增加对xss防护的安全系数。它有以下特点：
	    //setcookie()的第七个参数
	    //设为true后，只能通过http访问，javascript无法访问
	    //防止xss读取cookie
		simpleCookie.setHttpOnly(true);
		simpleCookie.setPath("/admin");
		//记住我cookie生效时间30天 ,单位秒;
		simpleCookie.setMaxAge(60*60*24*30);
		return simpleCookie;
	}
}
